﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using T.FAS.Runtime.Metadata.Dao;
using T.FAS.Runtime.Metadata.Entity;

namespace T.FAS.Runtime.Metadata.Client
{
    class MetaDataFirstCache : IMetaDataCache
    {
        private static object syncObj = new object();

        private static IMetaDataCache cache;

        private ConcurrentDictionary<string, IMetaData> storage;
        private ConcurrentDictionary<string, Tuple<string, string,string, string>> storageKey;
        private ConcurrentDictionary<string, long> storageTimeStamp;
        private ConcurrentDictionary<string, DateTime> cacheMetaDataValiadTime;
        private string TenantID;

        //public bool IsLocalModel { get; set; }

        private MetaDataFirstCache()
        {
            storage = new ConcurrentDictionary<string, IMetaData>(StringComparer.OrdinalIgnoreCase);
            storageKey = new ConcurrentDictionary<string, Tuple<string, string, string, string>>(StringComparer.OrdinalIgnoreCase);
            storageTimeStamp = new ConcurrentDictionary<string, long>();
            TenantID = UserUtil.GetCurrentUserTenantId();
            cacheMetaDataValiadTime = new ConcurrentDictionary<string, DateTime>();
        }

        public static IMetaDataCache GetSingleton()
        {
            if (cache == null)
            {
                lock (syncObj)
                {
                    if (cache == null)
                    {
                        cache = new MetaDataFirstCache();
                    }
                }
            }

            return cache;
        }

        public IMetaData Get(string metaDataId)
        {
            if (storage.ContainsKey(metaDataId))
            {
                return storage[metaDataId];
            }
            return null;
        }

        public bool IsCacheMetaDataValid(string metaDataId)
        {
            DateTime lastUsedTime;
            if (cacheMetaDataValiadTime.TryGetValue(metaDataId, out lastUsedTime))
            {
                if ((DateTime.Now - lastUsedTime).TotalSeconds > 10)
                {
                    cacheMetaDataValiadTime[metaDataId] = DateTime.Now;
                    return false;
                }
            }
            else
            {
                cacheMetaDataValiadTime[metaDataId] = DateTime.Now;
                return false;
            }
            return true;
        }


        public IMetaData TryGet(string metaDataId, out bool isSuccess)
        {
            try
            {
                var model = Get(metaDataId);
                isSuccess = true;

                return model;
            }
            catch
            {
                isSuccess = false;
                return null;
            }
        }
        public long GetTimeStamp(string metaDataId) {
            return storageTimeStamp[metaDataId];                  
        }
        public void Set(IMetaData domainModel)
        {
            if (domainModel != null)
            {
                storage[domainModel.ID] = domainModel;
                storageKey[getMetaDataKey(domainModel.Code, domainModel.MetaTypeID)] = new Tuple<string, string, string, string>(domainModel.ID, domainModel.ID, domainModel.ID, domainModel.CurrentVersion);
                var startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));
                storageTimeStamp[domainModel.ID] = (long)(domainModel.LastModifyTime - startTime).TotalSeconds;
                storageTimeStamp[domainModel.Code] = (long)(domainModel.LastModifyTime - startTime).TotalSeconds;
            }
        }

        public void Remove(string metaDataId)
        {
            if (storage.ContainsKey(metaDataId))
            {
                IMetaData temp = null;                
                storage.TryRemove(metaDataId, out temp);
                long timeStamp = 0;
                storageTimeStamp.TryRemove(metaDataId, out timeStamp);
            }
        }

        #region code type             
        public IMetaData Get(string metaDataCode, string metaTypeID)
        {
            if (storageKey.ContainsKey(getMetaDataKey(metaDataCode, metaTypeID))) {
                var metaKey = storageKey[getMetaDataKey(metaDataCode, metaTypeID)];
                if (metaKey!=null && string.IsNullOrEmpty(metaKey.Item1) == false
                    && storage.ContainsKey(metaKey.Item1)) {
                    return storage[metaKey.Item1];
                }
            }

            return null;
        }
        public IMetaData TryGet(string metaDataCode, string metaTypeID, out bool isSuccess)
        {
            try
            {
                var storageKey = GetCacheKey(metaDataCode, metaTypeID);
                var model = Get(storageKey.Item1);
                isSuccess = true;

                return model;
            }
            catch
            {
                isSuccess = false;
                return null;
            }
        }

        public void Remove(string metaDataCode, string metaTypeID)
        {

            string metaKey = getMetaDataKey(metaDataCode, metaTypeID);
            if (storageKey.ContainsKey(metaKey))
            {
                var meta = storageKey[metaKey];
                if (meta != null && string.IsNullOrEmpty(meta.Item1) == false && storage.ContainsKey(meta.Item1)) {

                    IMetaData temp = null;
                    storage.TryRemove(meta.Item1, out temp);
                    storageKey.TryRemove(metaKey, out meta);
                }
            }
        }
        public Tuple<string, string, string, string> GetCacheKey(string metaDataCode, string metaTypeID) {
            string key = getMetaDataKey(metaDataCode, metaTypeID);
            if (storage.ContainsKey(key))
            {
                IMetaData temp = null;
                return storageKey[key];
            }
            return null;
        }
        #endregion
        private string getMetaDataKey(string metaDataCode, string metaTypeID) {
            return string.Format("{0}:{1}-{2}", TenantID, metaDataCode, metaTypeID);
        }
        #region 未用到

        public Action<string> CacheSyncAction { get => (id) => Set(GetFallback(id)); }

        public void Add(IMetaData domainModel)
        {
            if (domainModel != null)
            {
                storage[domainModel.ID] = domainModel;

                storageKey[getMetaDataKey(domainModel.Code, domainModel.MetaTypeID)] = new Tuple<string, string, string, string>(domainModel.ID, domainModel.ID, domainModel.ID, domainModel.CurrentVersion);

                var startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));
                storageTimeStamp[domainModel.ID] = (long)(domainModel.LastModifyTime - startTime).TotalSeconds;
                storageTimeStamp[domainModel.Code] = (long)(domainModel.LastModifyTime - startTime).TotalSeconds;
            }
        }

        public IMetaData GetFallback(string metaDataId)
        {
            return MetaDataCacheFactory.GetCache(MetaDataCacheLevel.SecondLevel).Get(metaDataId);
        }

        public IMetaData GetFallback(string metaDataCode, string metaTypeID)
        {
            return MetaDataCacheFactory.GetCache(MetaDataCacheLevel.SecondLevel).Get(metaDataCode, metaTypeID);
        }

        public string GetLatestVersion(string metaDataId)
        {
            var model = Get(metaDataId);
            return model == null ? "-1" : model.CurrentVersion;
        }

        public string GetLatestVersion(string metaDataCode, string metaTypeID)
        {
            var model = GetCacheKey(metaDataCode, metaTypeID);
            return model == null ? "-1" : model.Item4;
        }
        #endregion
    }
}
